package transaction

import (
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric-protos-go/common"
	"github.com/hyperledger/fabric-protos-go/msp"
	pb "github.com/hyperledger/fabric-protos-go/peer"
	"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger"
	"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"

	"bcb-cc-observ/pkg/consts"
	"bcb-cc-observ/pkg/models"
)

func GetTransactionInfo(client *ledger.Client, txid string) (*models.F_Transaction_Info, error) {
	var blk *models.F_Transaction_Info
	var processedTx *pb.ProcessedTransaction

	blk = &models.F_Transaction_Info{}
	processedTx, err := client.QueryTransaction((fab.TransactionID)(txid))
	if err != nil {
		return nil, err
	}
	blk.TxId = txid
	// 0 表示 有效
	if 0 == int(processedTx.ValidationCode) {
		blk.ValidationCode = "VALID"
	} else {
		blk.ValidationCode = "UNKOWN"
	}

	// -------------------- payload ---------------
	envelope := processedTx.TransactionEnvelope
	payload := &common.Payload{}
	err = proto.Unmarshal(envelope.Payload, payload)
	if err != nil {
		return nil, err
	}

	// -------------------- channel header ---------------
	channelHeader := &common.ChannelHeader{}
	err = proto.Unmarshal(payload.Header.ChannelHeader, channelHeader)
	if err != nil {
		return nil, err
	}
	blk.ChannelId = channelHeader.ChannelId
	blk.TypeStr = consts.HeaderType_name[int32(channelHeader.Type)]
	blk.TimeStr = channelHeader.Timestamp.AsTime().Local().Format("2006-01-02 15:04:05.000")

	// cc
	cc, _ := getChaincode(envelope)
	blk.ChainCode = cc.ChaincodeName

	// msp id
	mspId, _ := getCreatorMspId(envelope)
	blk.CreatorMsp = *mspId

	// creator user
	cert, _ := getCertificate(envelope)
	blk.CreatorUser = cert.Subject.CommonName

	// cc call args
	blk.Args, _ = getArgs(envelope)

	// 读写集合
	rwsets, _ := getRWSet(envelope)
	blk.RWSets = *rwsets

	// 背书
	endorsers, proposalHash, err := getEndorsers(envelope)
	if err != nil {
		return nil, err
	}

	blk.Endorsers = endorsers
	blk.PayloadProposalHash = *proposalHash

	return blk, nil
}

func getCertificate(envelope *common.Envelope) (*x509.Certificate, error) {
	payload := &common.Payload{}
	err := proto.Unmarshal(envelope.Payload, payload)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("|---> payload\n%#v\n", payload)

	// -------------------- channel header ---------------
	channelHeader := &common.ChannelHeader{}
	err = proto.Unmarshal(payload.Header.ChannelHeader, channelHeader)
	if err != nil {
		return nil, err
	}
	// channel Header extension 解析
	chaincodeHeaderExtension := &pb.ChaincodeHeaderExtension{}
	err = proto.Unmarshal(channelHeader.Extension, chaincodeHeaderExtension)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("---> ChannelHeader extension\n%#v\n", chaincodeHeaderExtension)
	//fmt.Printf("---> ChannelHeader extension, chaincodeID\n%#v\n", chaincodeHeaderExtension.ChaincodeId)
	//fmt.Printf("---> ChannelHeader extension, chaincodeID\n%#v\n", chaincodeHeaderExtension.ChaincodeId.Name)

	signatureHeader := &common.SignatureHeader{}
	//-------------------- channel header ---------------
	err = proto.Unmarshal(payload.Header.SignatureHeader, signatureHeader)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("---> signatureHeader\n%#v\n", signatureHeader)
	//fmt.Printf("---> signatureHeader creator\n%#v\n", string(signatureHeader.Creator))

	//type SerializedIdentity struct {
	//Mspid   string
	//IdBytes []byte
	//}

	creator := &msp.SerializedIdentity{}
	err = proto.Unmarshal(signatureHeader.Creator, creator)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("---> signatureHeader creator\n%#v\n", creator.Mspid)

	uEnc := base64.URLEncoding.EncodeToString([]byte(creator.IdBytes))

	certText, err := base64.URLEncoding.DecodeString(uEnc)
	if err != nil {
		return nil, err
	}

	end, _ := pem.Decode([]byte(string(certText)))
	if end == nil {
		return nil, err
	}
	cert, err := x509.ParseCertificate(end.Bytes)
	if err != nil {
		return nil, err
	}
	//fmt.Println(cert.Subject)

	return cert, nil
}

func getCreatorMspId(envelope *common.Envelope) (*string, error) {
	payload := &common.Payload{}
	err := proto.Unmarshal(envelope.Payload, payload)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("|---> payload\n%#v\n", payload)

	// -------------------- channel header ---------------
	channelHeader := &common.ChannelHeader{}
	err = proto.Unmarshal(payload.Header.ChannelHeader, channelHeader)
	if err != nil {
		return nil, err
	}
	// channel Header extension 解析
	chaincodeHeaderExtension := &pb.ChaincodeHeaderExtension{}
	err = proto.Unmarshal(channelHeader.Extension, chaincodeHeaderExtension)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("---> ChannelHeader extension\n%#v\n", chaincodeHeaderExtension)
	//fmt.Printf("---> ChannelHeader extension, chaincodeID\n%#v\n", chaincodeHeaderExtension.ChaincodeId)
	//fmt.Printf("---> ChannelHeader extension, chaincodeID\n%#v\n", chaincodeHeaderExtension.ChaincodeId.Name)

	signatureHeader := &common.SignatureHeader{}
	//-------------------- channel header ---------------
	err = proto.Unmarshal(payload.Header.SignatureHeader, signatureHeader)
	if err != nil {
		return nil, err
	}

	//fmt.Printf("---> signatureHeader\n%#v\n", signatureHeader)
	//fmt.Printf("---> signatureHeader creator\n%#v\n", string(signatureHeader.Creator))

	creator := &msp.SerializedIdentity{}
	err = proto.Unmarshal(signatureHeader.Creator, creator)
	if err != nil {
		return nil, err
	}
	//fmt.Printf("---> signatureHeader creator\n%#v\n", creator.Mspid)

	return &creator.Mspid, nil
}
